package com.qingsong.component;

import lombok.Getter;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;

/**
 * @author qingsong
 */

@Getter
@Component
public class MyEs {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    public MyEs(ElasticsearchRestTemplate elasticsearchRestTemplate) {
        this.elasticsearchRestTemplate = elasticsearchRestTemplate;
    }

    public <T> T saveOrUpdate(T entity) {
        return elasticsearchRestTemplate.save(entity);
    }

    public <T> List<T> saveOrUpdate(List<T> entities) {
        Iterable<T> result = elasticsearchRestTemplate.save(entities);
        List<T> list = new ArrayList<>();
        for (T e : result) {
            list.add(e);
        }
        return list;
    }

    /**
     * 根据主键查询一条文档数据
     *
     * @param id 文档主键
     * @return 主键存在时返回文档数据，主键不存在返回nuLL
     * @paramcLazz返回值泛型类型
     */

    public <T> T get(String id, Class<T> clazz) {
        return elasticsearchRestTemplate.get(id, clazz);
    }

    /**
     * 根据主键查询一条文档数据是否存在
     *
     * @param id 文档主键
     * @return true存在，false不存在
     * @paramcLazz返回值泛型类型
     */
    public boolean exists(String id, Class<?> clazz) {
        return elasticsearchRestTemplate.exists(id, clazz);
    }

    public String delete(String id, Class<?> clazz) {
        return elasticsearchRestTemplate.delete(id, clazz);
    }

    /**
     * 根据MatchQuery条件查询满足条件的文档数量
     *
     * @param QueryBuilder MatchQuery条件查询器，可通过 QueryBuilders 静态创建
     * @param clazz        返回值泛型类型
     * @return 满足条件的文档数量
     */
    public long count(QueryBuilder QueryBuilder, Class<?> clazz) {
        Query query = new NativeSearchQueryBuilder().withQuery(QueryBuilder).build();
        return elasticsearchRestTemplate.count(query, clazz);
    }

    /**
     * 根据条件查询满足条件的文档数量
     *
     * @param queryBuilders 条件对象，如 QueryBuilders.matchQuery（"name"，"赵四"）等
     * @param clazz         返回值泛型类型
     * @return满足条件的文档数量 !!! 废弃的, 会覆盖
     */
    public long count(List<QueryBuilder> queryBuilders, Class<?> clazz) {
        //构建本地搜索器
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //搜索条件
        for (QueryBuilder queryBuilder : queryBuilders) {
            nativeSearchQueryBuilder.withQuery(queryBuilder);
        }
        //根据条件查询满足条件的文档数量
        return elasticsearchRestTemplate.count(nativeSearchQueryBuilder.build(), clazz);
    }

    /**
     * 根据条件查询满足条件的文档
     *
     * @param queryBuilder 条件对象，如 QueryBuilders.matchQuery("name"，“赵四"）等
     * @param clazz        返回值泛型类型
     * @param pageNum      当前展示第几页，ES中的分页是从θ开始的
     * @param pageSize     每页展示多少条
     * @param sortField    按哪个字段进行排序
     * @param sortType     升序还是降序，可选“asc”和“desc“
     * @return满足条件的文档数量
     */
    public <T> List<T> search(QueryBuilder queryBuilder,
                              Class<T> clazz,
                              int pageNum, int pageSize,
                              String sortField, String sortType) {
        //pageNum值小于1时均视为是1
        pageNum = Math.max(pageNum, 1);
        //ES中的分页是从0开始的
        pageNum = pageNum - 1;
        //构建本地搜索器Builder对象
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //添加搜索条件
        nativeSearchQueryBuilder.withQuery(queryBuilder);
        //添加分页条件
        nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNum, pageSize));
        //添加排序条件
        SortOrder sortOrder = "desc".equalsIgnoreCase(sortType) ? SortOrder.DESC : SortOrder.ASC;
        nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField).order(sortOrder));
        //构建本地搜索器
        NativeSearchQuery query = nativeSearchQueryBuilder.build();
        //根据条件查询满足条件的文档
        SearchHits<T> searchHits = elasticsearchRestTemplate.search(query, clazz);
        // 将搜索结果转为List类型的数据并返回
        List<T> result = new ArrayList<>();
        for (SearchHit<T> searchHit : searchHits) {
            result.add(searchHit.getContent());
        }
        return result;
    }


    /**
     * 根据条件查询满足条件的文档数量, 默认以得分降序
     * @param queryBuilder
     * @param clazz
     * @param pageNum
     * @param pageSize
     * @return
     * @param <T>
     */
    public <T> List<T> search(QueryBuilder queryBuilder, Class<T> clazz,
                              int pageNum, int pageSize) {
        return this.search(queryBuilder, clazz, pageNum, pageSize, "_score", "desc");
    }

    /**
     * 根据条件查询满足条件的文档数量, 默认展示第一页的10条数据
     * @param queryBuilder
     * @param clazz
     * @param sortField
     * @param sortType
     * @return
     * @param <T>
     */
    public <T > List < T > search(QueryBuilder queryBuilder, Class < T > clazz,
                String sortField, String sortType) {
        return this.search(queryBuilder, clazz, 1, 10, sortField, sortType);
    }

    /**
     * 根据条件查询满足条件的文档数量, 默认以得分降序, 默认展示第一页的10条数据
     * @param queryBuilder
     * @param clazz
     * @return
     * @param <T>
     */
    public <T > List < T > search(QueryBuilder queryBuilder, Class < T > clazz) {
        return this.search(queryBuilder, clazz, 1, 10, "_score", "desc");
    }

 }